package main

import (
	"gateway/src/common"
	_ "gateway/src/common"
	"gateway/src/specialHandle"
	"gateway/src/util"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

type ProxyHandler struct{}
type mapInterface map[string]interface{}

func (*ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" { //视频说chrome会访问一个图标文件，此时不做负载均衡及代理处理
		return
	}
	//首先根据url找到对应的负载均衡器

	urlSlice := getUrlPathSlice(r.URL.Path)
	//获取对应的负载均衡器
	lbPath := "/" + urlSlice[1]
	lb := util.LoadBalanceList[lbPath]
	var pass = ""
	//将第二个/开始的内容拼接到路由结果的后面。
	//获取负载均衡器path后面的内容，并拼接成字符串
	if len(urlSlice) > 2 { //确保不报空指针，就是确保有值
		pass = "/" + strings.Join(urlSlice[2:], "/") //a[1:4] 表示切片中的三个元素a[1],a[2],a[3] go切片左闭右开
	}
	//根据负载均衡配置选择正确的路由
	Url, _ := url.Parse(lb.GetServer(r.RemoteAddr).Host + pass)

	proxy := httputil.NewSingleHostReverseProxy(Url)
	//启动过滤链
	specialHandle.AddWebFilterFunc(F2, F3) //添加在proxy这里创建的过滤器内容
	if result := specialHandle.StartWebFilter(w, r); result {
		proxy.ServeHTTP(w, r)
	}

	//http.HandleFunc("/", ProxyRequestHandler(proxy))

	defer func() {
		if err := recover(); err != nil {
			//设置Header
			w.WriteHeader(500)
			w.Write([]byte("<h1>出错了</h1>"))
			proxy.ServeHTTP(w, r)
			log.Println("test:", err)
		}
	}()

}
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		proxy.ServeHTTP(w, r)
	}
}
func main() {
	log.Println("start gateway on port:", util.ProxyConfigs["port"])
	err := http.ListenAndServe(":"+util.ProxyConfigs["port"], &ProxyHandler{})
	if err != nil {
		log.Fatal(err)
		return
	}

}
func CloneHead(src http.Header, dest *http.Header) {
	for k, v := range src {
		//这里只设置了第一个值
		dest.Set(k, v[0])
	}
}
func RequestUrl(w http.ResponseWriter, r *http.Request, url string) {
	newReq, _ := http.NewRequest(r.Method, url, r.Body)
	//将当前客户端的请求头拷贝给远端服务器
	CloneHead(r.Header, &newReq.Header)
	//在代理中需要手动设置进去
	newReq.Header.Add("X-Forwarded-For", r.RemoteAddr)
	//使用go自带的反向代理工具 实现了响应头的自动拷贝
	dt := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		ResponseHeaderTimeout: 1 * time.Second,
	}

	newResponse, err := dt.RoundTrip(newReq)
	if err != nil {
		log.Println(err)
	}
	//newResponse,_:=http.DefaultClient.Do(newReq)
	header := w.Header()
	//将源网页的响应头拷贝给客户端
	CloneHead(newResponse.Header, &header)
	//写入请求头
	w.WriteHeader(newResponse.StatusCode)
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatal(err)
		}
	}(newResponse.Body)
	finalResponse, _ := io.ReadAll(newResponse.Body)
	w.Write(finalResponse)

}
func getUrlPathSlice(url_path string) []string {
	return strings.Split(url_path, "/")
}

// F2 类似这样就可以在各个地方添加过滤器/责任链了
func F2(ctx *specialHandle.MyContext, w http.ResponseWriter, r *http.Request) {
	data := make(mapInterface) //只能这样处理，否则会报panic: assignment to entry in nil map
	/*data["test1"]="test1"
	data["test2"]="test2"*/

	w.WriteHeader(502)
	_, err := w.Write(common.Fail(data, "权限不足"))
	if err != nil {
		log.Panicln(err)
	}
	ctx.KeyValue["key"] = false
}

func F3(ctx *specialHandle.MyContext, w http.ResponseWriter, r *http.Request) {
	ctx.KeyValue["key"] = false
}
